# -*- coding:utf-8 -*-

import os
import sys
import shutil
import time
import thread
import threading
import zipfile
import glob
import signal
from hashlib import md5

from MyException import Error
from MyException import GetSizeError
from MyException import NotFileError
from MyException import BigFileError
from MyException import BigZipError
from MyException import NonZipError
from MyException import CopyFileError
from MyLogger import MyLogger, SIZEROTATE, TIMEROTATE
from StatLog import StatLog
from ConfigInfo import Profile
import GlobalVar as GlobalVar

class MySend(threading.Thread):
    def __init__(self, profile,type,mutex,num):
        threading.Thread.__init__(self)
        self.type = type
        self.profile = profile
        self.work_dir = profile.recv_zip
        self.prefix = type
        self.mutex = mutex
        self.num = num
        self.send_dir = ""
        if (type == "Bigdata"):
            self.send_dir = profile.send_bigdata
            self.time_ctrl = True
        elif (type == "Keyword"):
            self.send_dir = profile.send_keyword
            self.time_ctrl = False
        elif (type == "Mass"):
            self.send_dir = profile.send_mass
            self.time_ctrl = False

        self.run_logger = MyLogger(os.path.join(profile.log_dir, "unzip_" + self.type + ".log"), SIZEROTATE, profile.size_rotate_backup_num)
        self.run_logger.set_log_lev(profile.log_lev)

    def run(self):

        self.run_logger.info("[%s Send] Thread started, Recv Dir : %s Send dir ; %s" % (self.type, self.work_dir, self.send_dir))

        while GlobalVar.get_run_flag():
            time.sleep(1)

            # self.run_logger.debug("[UNZIP %s Thread %s] acquire mutex" % (self.type, str(self.num)))
            self.mutex.acquire()
            # self.run_logger.debug("[UNZIP %s Thread %s] acquire mutex sucess" % (self.type, str(self.num)))

            # 获得所有的zip文件
            zip_files = glob.glob('%s/%s_*.zip' % (self.work_dir, self.prefix))
            if len(zip_files) == 0 :
                # self.run_logger.debug("[UNZIP %s Thread %s] no zip file recevied" % (self.type, self.num))
                self.mutex.release()
                continue

            # 时间控制逻辑,在hadoop晚上工作的时候，不解压Bigdata_*.zip，转存到cache_dir
            if self.time_ctrl:
                # 判断是不是已经进入hadoop工作时间范围了
                start_time_str = time.strftime("%Y%m%d ", time.localtime()) + self.profile.unzip_bigdata_start
                end_time_str = time.strftime("%Y%m%d ", time.localtime()) + self.profile.unzip_bigdata_end

                self.run_logger.debug("[UNZIP %s Thread %s] Unzip %s thread working hours from %s to %s"%(self.type, self.num, self.type, start_time_str, end_time_str))

                # 返回False表示hadoop在工作，不能解压
                if not self.compare_time(start_time_str, end_time_str):
                    self.run_logger.info("[UNZIP %s Thread %s] Hadoop is working ,move zip files to %s"%(self.type, self.num, self.profile.cache_dir))
                    # 移动zip文件到cache_dir
                    self.move_zipfiles_to_cache_dir(zip_files)
                    self.run_logger.debug("[UNZIP %s Thread %s] Move zip files to %s done"%(self.type, self.num, self.profile.cache_dir))
                    # continue继续循环，不执行解压业务，再此之前要释放锁
                    self.mutex.release()
                    continue
                else:
                    self.run_logger.debug("[UNZIP %s Thread %s] Unzip Thread is in working hours")


            # 针对每一个zip文件建立临时目录，并mv到临时目录
            tmp_zip_files = [] #保存所有在临时目录中的zip文件路径
            try:
                for zip_file_path in zip_files :
                    # 拼接文件名
                    filename = os.path.split(zip_file_path)[1]
                    # 临时目录
                    dst_dir = os.path.join(self.send_dir, filename.split(".")[0])
                    if not os.path.exists(dst_dir):
                        os.makedirs(dst_dir)
                        self.run_logger.debug("[UNZIP %s Thread %s] mkdir sucess : %s"%(self.type, self.num, dst_dir))
                    # 临时文件路径
                    dst_zip_path = os.path.join(dst_dir, filename)
                    dst_zip_path_tmp = dst_zip_path + ".tmp"
                    # 拷贝文件到临时目录
                    try:
                        shutil.copyfile(zip_file_path, dst_zip_path_tmp)
                        self.run_logger.info("[UNZIP %s Thread %s] copy zip file sucess : %s" % (self.type, self.num, dst_zip_path_tmp))
                        os.rename(dst_zip_path_tmp, dst_zip_path)
                        self.run_logger.info("[UNZIP %s Thread %s] rename zip file sucess : %s" % (self.type, self.num, dst_zip_path))
                        os.remove(zip_file_path)
                        self.run_logger.info("[UNZIP %s Thread %s] remove old zip file sucess" % (self.type, self.num))
                        tmp_zip_files.append(dst_zip_path)
                    except:
                        self.run_logger.error("[UNZIP %s Thread %s] fail to move file : %s"%(self.type, self.num, zip_file_path))
                        #os.kill(os.getpid(), signal.SIGTERM)
                        if os.path.exists(dst_dir):
                            shutil.rmtree(dst_dir)
                        raise
            except:
                self.run_logger.error("[UNZIP %s Thread %s] move files failed"%(self.type, self.num))
                self.mutex.release()

            self.mutex.release()

            for zip_path in tmp_zip_files:
                self.unzip(zip_path, self.send_dir)
                name = os.path.split(zip_path)[1]
                tmp_dir = os.path.split(zip_path)[0]
                self.run_logger.info("[UNZIP %s Thread %s] unzip %s sucess"%(self.type, self.num, name))
                shutil.rmtree(os.path.split(zip_path)[0])
                self.run_logger.debug("[UNZIP %s Thread %s] remove temp dir sucess : %s"%(self.type, self.num, tmp_dir))


    def md5_for_file(self, file):
        m = md5()
        f = open(file, 'rb')
        buffer = 8192

        while True:
            chunk = f.read(buffer)
            if not chunk : break
            m.update(chunk)

        f.close()
        return m.hexdigest()

    def unzip(self, zip_path, output_dir):
        # 获得zip文件所在目录，和zip文件名
        zip_dir = os.path.split(zip_path)[0]
        zip_name = os.path.split(zip_path)[1]
        # 解压文件
        zip = zipfile.ZipFile(zip_path)
        try:
            zip.extractall(zip_dir)
        except Exception, e:
            self.run_logger.error("[UNZIP %s Thread %s] fail to extract files"%(self.type, self.num))
            self.run_logger.error("[UNZIP %s Thread %s] %s"%(self.type, self.num, repr(e)))
            #os.kill(os.getpid(), signal.SIGTERM)
        zip.close()
        self.run_logger.info("[UNZIP %s Thread %s] unzip file %s sucess"%(self.type, self.num, zip_name))

        # 打开md5list文件
        md5list_file_path = os.path.join(zip_dir, "md5list")
        md5list_file_obj = open(md5list_file_path, "r")

        # 读md5list，校验文件
        while True:
            line = md5list_file_obj.readline()
            if not line:
                self.run_logger.debug("[UNZIP %s Thread %s] md5list read done"%(self.type, self.num))
                break

            line = line.strip('\n')

            str_list = line.split(" ")
            if len(str_list) != 2 :
                self.run_logger.error("[UNZIP %s Thread %s] bad md5list file"%(self.type, self.num))
                break

            file_path = os.path.join(zip_dir, str_list[1])
            try:
                md5 = self.md5_for_file(file_path)
                self.run_logger.info("[UNZIP %s Thread %s] md5sum generate success, %s"%(self.type, self.num, str_list[1]))
            except:
                self.run_logger.error("[UNZIP %s Thread %s] md5sum failed, %s"%(self.type, self.num, str_list[1]))
                self.run_logger.error("[UNZIP %s Thread %s] line: %s"%(self.type, self.num, line))
                #os.kill(os.getpid(), signal.SIGTERM)
                continue

            if str(md5) == str_list[0] :
                self.run_logger.info("[UNZIP %s Thread %s] md5sum check sucess, file: %s"%(self.type, self.num, str_list[1]))
                # 拷贝文件
                dst_file = os.path.join(output_dir, str_list[1])
                # 修改日期，无论文件是什么日期，将它修改为今天
                if self.type == 'Bigdata':
                    dst_file = self.ajust_date(dst_file)
                    self.run_logger.debug("[UNZIP %s Thread %s] dst file : %s"%(self.type, self.num, dst_file))
                dst_file_tmp = dst_file + ".tmp"
                shutil.copyfile(file_path, dst_file_tmp)
                os.rename(dst_file_tmp, dst_file)
                self.run_logger.info("[UNZIP %s Thread %s] copy file sucess : %s"%(self.type, self.num, dst_file))
            else:
                self.run_logger.error("[UNZIP %s Thread %s] md5sum check failed, file: %s"%(self.type, self.num, str_list[1]))
                #os.kill(os.getpid(), signal.SIGTERM)

    def compare_time(self, start_time_str, end_time_str):
        """
        比较当前时间与start_time_str, 和end_time_str，如果在它们中间返回True，否则返回False
        :param start_time_str: 开始时间，格式为"yyyyMMdd HH:MM"
        :param end_time_str: 结束时间，格式同上
        :return: 
        """
        s_time = None
        c_time = None
        e_time = None
        try:
            s_time = time.mktime(time.strptime(start_time_str,'%Y%m%d %H:%M'))
            e_time = time.mktime(time.strptime(end_time_str,'%Y%m%d %H:%M'))
            c_time = time.time()
        except Exception, e:
            self.run_logger.error("[UNZIP %s Thread %s] fail to mktime"%(self.type, self.num))
            self.run_logger.error("[UNZIP %s Thread %s] %s"%(self.type,self.num, repr(e)))
            #os.kill(os.getpid(), signal.SIGKILL)

        # self.run_logger.debug("[UNZIP %s Thread %s] s_time ; %s"%(self.type, self.num, str(s_time)))
        # self.run_logger.debug("[UNZIP %s Thread %s] e_time ; %s"%(self.type, self.num, str(e_time)))
        # self.run_logger.debug("[UNZIP %s Thread %s] c_time ; %s"%(self.type, self.num, str(c_time)))

        if (float(c_time) > float(s_time)) and (float(c_time) < float(e_time)):
            return True
        else:
            return False

    def move_zipfiles_to_cache_dir(self, zipfiles):
        """
        移动zip文件到cache_dir，在时间控制逻辑里被调用
        :param zipfiles: 待移动的文件列表
        :return: 
        """
        for file in zipfiles:
            # 拼接文件名
            filename = os.path.split(file)[1]
            dst_file_path = os.path.join(self.profile.cache_dir, filename)
            tmp_file_path = dst_file_path + ".tmp"

            self.run_logger.info("[UNZIP %s Thread %s] ready to move %s to %s"%(self.type, self.num, filename, self.profile.cache_dir))
            try:
                # move文件为tmp_file_path
                shutil.copyfile(file, tmp_file_path)
                # rename 临时文件tmp_file_path为dst_file_path
                os.rename(tmp_file_path, dst_file_path)
            except Exception, e:
                self.run_logger.error("[UNZIP %s Thread %s] move zip file to cache_dir failed"%(self.type, self.num))
                self.run_logger.error("[UNZIP %s Thread %s] %s"%(self.type, self.num, repr(e)))
            # 删除原文件
            os.remove(file)

    def ajust_date(self, filepath):
        """
        针对hadoop，将文件名中的日期修改为今天
        :param filepath: 
        :return: 
        """
        # filepath 样例：/testwsf/testwsf/@20170526@HTTP@20170526@20170526_HTTP_20170526_000C29986AEA_3056201_001.001.001.001.nb
        old_date = filepath.split('@')[1]
        cur_date = time.strftime("%Y%m%d", time.localtime())

        return filepath.replace(old_date, cur_date)